﻿using Java.Net;
using Java.Util;
using SharpDepend.Datatypes;
using System;
using System.Net;
using System.Net.Sockets;

namespace SharpDepend.Android
{
    /// <summary>
    /// Reference: http://www.jarloo.com/c-udp-multicasting-tutorial/
    /// </summary>
    class MulticastManager : ISocketMulticastManager
    {
        class SocketMulticast : ISocketMulticast
        {
            // A client receiver for multicast traffic from any source
            MulticastSocket _client = null;

            // true if we have joined the multicast group; otherwise, false
            bool _joined = false;

            // Buffer for incoming data
            private byte[] _receiveBuffer;

            // Maximum size of a message in this communication
            private const int MAX_MESSAGE_SIZE = 512;

            public SocketMulticast()
            {
                // Turn off multicast filter.
                // Reference: http://stackoverflow.com/questions/4589946/problem-with-multicastsocket-on-java-android
                //Android.Net.Wifi.WifiManager.MulticastLock mcastLock = new Android.Net.Wifi.WifiManager.MulticastLock();
                //mcastLock.acquire();
            }

            NetworkInterface TryFindNetworkInterface()
            {
                //IEnumeration enumeration = NetworkInterface.NetworkInterfaces;
                //NetworkInterface eth0 = null;
                //while (enumeration.HasMoreElements)
                //{
                //    eth0 = (NetworkInterface)enumeration.NextElement();
                //    if (eth0.getName().equals("eth0"))
                //    {
                //        //there is probably a better way to find ethernet interface
                //        break;
                //    }
                //}

                return NetworkInterface.GetByName("eth0");
            }

            /// <summary>
            /// Create a new UdpAnySourceMulticastClient instance and join the group.
            /// Reference: http://stackoverflow.com/questions/8180275/multicast-no-such-device
            /// </summary>
            private BoolResult Join(string ip, int port)
            {
                try
                {
                    // Initialize the receive buffer
                    _receiveBuffer = new byte[MAX_MESSAGE_SIZE];

                    InetAddress address = InetAddress.GetByName(ip);

                    // Create the UdpAnySourceMulticastClient instance using the defined 
                    // GROUP_ADDRESS and GROUP_PORT constants. UdpAnySourceMulticastClient is a 
                    // client receiver for multicast traffic from any source, also known as Any Source Multicast (ASM)
                    _client = new MulticastSocket(port);

                    NetworkInterface eth0 = TryFindNetworkInterface();
                    _client.JoinGroup(new InetSocketAddress(address, port), eth0);

                    // The MulticastLoopback property controls whether you receive multicast 
                    // packets that you send to the multicast group. Default value is true, 
                    // meaning that you also receive the packets you send to the multicast group. 
                    // To stop receiving these packets, you can set the property following to false
                    _client.LoopbackMode = true;

                    // Set a flag indicating that we have now joined the multicast group 
                    _joined = true;

                    return BoolResult.Succeded;
                }
                catch (Exception error)
                {
                    return new BoolResult(error);
                }
            }

            public BoolResult CreateMulticastSender(string ip, int port)
            {
                return Join(ip, port);
            }

            public BoolResult CreateMulticastListener(string ip, int port)
            {
                return Join(ip, port);
            }

            public void Write(byte[] data, int length)
            {
                // Attempt the send only if you have already joined the group.
                if (_joined)
                {
                    DatagramPacket dp = new DatagramPacket(data, data.Length);
                    _client.Send(dp);
                }
                else
                {
                    Log.Warning(this, "Multicast write failed since no group is joined.");
                }
            }

            public void Read(MulticastListenerResult result)
            {
                // Only attempt to receive if you have already joined the group
                if (_joined)
                {
                    Array.Clear(_receiveBuffer, 0, _receiveBuffer.Length);

                    //byte[] buffer = new byte[65535];
                    DatagramPacket receivedPacket = new DatagramPacket(_receiveBuffer, _receiveBuffer.Length);
                    _client.Receive(receivedPacket);

                    result(receivedPacket.GetData());
                }
                else
                {
                    Log.Warning(this, "Multicast receiving failed. Not joined to the group.");
                }
            }

            public void Dispose()
            {
                _client.Dispose();
            }
        }

        public ISocketMulticast CreateMulticast()
        {
            return new SocketMulticast();
        }
    }
}
